#include "sqlite_wrapper.h"
#include <cstring>
#include <utility>
#include <string>

DatabaseConnection::DatabaseConnection(std::string_view db_path) noexcept : db_(nullptr)
{
    // Convert string_view to string to ensure null termination for sqlite3_open
    std::string db_path_str(db_path);
    int result = sqlite3_open(db_path_str.c_str(), &db_);
    if (result != SQLITE_OK)
    {
        // If opening fails, set db_ to nullptr
        if (db_)
        {
            sqlite3_close(db_);
            db_ = nullptr;
        }
    }
}

DatabaseConnection::~DatabaseConnection() noexcept
{
    if (db_)
    {
        sqlite3_close(db_);
        db_ = nullptr;
    }
}

std::string DatabaseConnection::get_error_message() const noexcept
{
    if (db_)
    {
        return std::string(sqlite3_errmsg(db_));
    }
    return "Invalid database connection";
}

PreparedStatement::PreparedStatement(DatabaseConnection& db, std::string_view sql) noexcept : stmt_(nullptr), has_row_(false)
{
    int result = sqlite3_prepare_v2(db.get(), sql.data(), static_cast<int>(sql.size()), &stmt_, nullptr);
    if (result != SQLITE_OK)
    {
        // If preparation fails, stmt_ will be nullptr
        if (stmt_)
        {
            sqlite3_finalize(stmt_);
            stmt_ = nullptr;
        }
    }
}

PreparedStatement::~PreparedStatement() noexcept
{
    if (stmt_)
    {
        sqlite3_finalize(stmt_);
        stmt_ = nullptr;
    }
}

DbError PreparedStatement::bind_int(int index, int value) noexcept
{
    if (!stmt_)
    {
        return DbError::INVALID_INPUT;
    }

    int result = sqlite3_bind_int(stmt_, index, value);
    if (result != SQLITE_OK)
    {
        return DbError::BIND_FAILED;
    }
    return DbError::SUCCESS;
}

DbError PreparedStatement::bind_int64(int index, std::int64_t value) noexcept
{
    if (!stmt_)
    {
        return DbError::INVALID_INPUT;
    }

    int result = sqlite3_bind_int64(stmt_, index, value);
    if (result != SQLITE_OK)
    {
        return DbError::BIND_FAILED;
    }
    return DbError::SUCCESS;
}

DbError PreparedStatement::bind_text(int index, std::string_view value) noexcept
{
    if (!stmt_)
    {
        return DbError::INVALID_INPUT;
    }

    // SQLITE_STATIC means SQLite will not copy the data and will use the pointer directly
    // This is safe because we're passing a string_view that should remain valid during the bind operation
    int result = sqlite3_bind_text(stmt_, index, value.data(), static_cast<int>(value.size()), SQLITE_STATIC);
    if (result != SQLITE_OK)
    {
        return DbError::BIND_FAILED;
    }
    return DbError::SUCCESS;
}

DbError PreparedStatement::step() noexcept
{
    if (!stmt_)
    {
        return DbError::INVALID_INPUT;
    }

    int result = sqlite3_step(stmt_);
    if (result == SQLITE_DONE)
    {
        has_row_ = false;
        return DbError::SUCCESS;
    }
    else if (result == SQLITE_ROW)
    {
        has_row_ = true;
        return DbError::SUCCESS;
    }
    else
    {
        has_row_ = false;
        return DbError::EXECUTION_FAILED;
    }
}

int PreparedStatement::changes() const noexcept
{
    // We need to get the database connection from the statement
    if (stmt_)
    {
        sqlite3* db = sqlite3_db_handle(stmt_);
        if (db)
        {
            return sqlite3_changes(db);
        }
    }
    return 0;
}

std::int64_t PreparedStatement::last_insert_rowid() const noexcept
{
    if (stmt_)
    {
        sqlite3* db = sqlite3_db_handle(stmt_);
        if (db)
        {
            return sqlite3_last_insert_rowid(db);
        }
    }
    return 0;
}

int PreparedStatement::column_count() const noexcept
{
    if (stmt_)
    {
        return sqlite3_column_count(stmt_);
    }
    return 0;
}

int PreparedStatement::column_int(int index) const noexcept
{
    if (stmt_ && index >= 0 && index < column_count())
    {
        return sqlite3_column_int(stmt_, index);
    }
    return 0;
}

std::int64_t PreparedStatement::column_int64(int index) const noexcept
{
    if (stmt_ && index >= 0 && index < column_count())
    {
        return sqlite3_column_int64(stmt_, index);
    }
    return 0;
}

std::string_view PreparedStatement::column_text(int index) const noexcept
{
    if (stmt_ && index >= 0 && index < column_count())
    {
        const char* text = reinterpret_cast<const char*>(sqlite3_column_text(stmt_, index));
        if (text)
        {
            int size = sqlite3_column_bytes(stmt_, index);
            return std::string_view(text, static_cast<size_t>(size));
        }
    }
    return std::string_view();
}

DbError PreparedStatement::reset() noexcept
{
    if (!stmt_)
    {
        return DbError::INVALID_INPUT;
    }

    int result = sqlite3_reset(stmt_);
    if (result != SQLITE_OK)
    {
        return DbError::EXECUTION_FAILED;
    }

    // Also clear any bindings
    result = sqlite3_clear_bindings(stmt_);
    if (result != SQLITE_OK)
    {
        return DbError::EXECUTION_FAILED;
    }

    has_row_ = false;
    return DbError::SUCCESS;
}